#ifndef VL_HYPERCUBE_HPP
#define VL_HYPERCUBE_HPP

#error "This is not working!"

#include "vec.hpp"

namespace vl
{

#define VL_DIM_FUNC(rettype, name, param, def) \
	struct name { template<int Dim> static rettype go def };
	

template<int Dims, typename T>
struct basic_hypercube
{
	struct interval
	{
		interval()
		{
		}
		
		interval(T b, T e)
		: b(b), e(e)
		{
		}
		
		T b, e;
	};
	
	template<int Dim, typename Functor>
	struct each_dim
	{
		static void go(basic_hypercube& b)
		{
			Functor::template go<Dim>(b);
			each_dim<Dim + 1, Functor>::go(b);
		}
	};
	
	template<>
	struct each_dim<Dims>
	{
		static void go(basic_hypercube&)
		{}
	};
	
	template<int Dim, typename Functor>
	struct each_dim_2
	{
		static void go(basic_hypercube& rhs, basic_hypercube& lhs)
		{
			Functor::template go<Dim>(rhs, lhs);
			each_dim<Dim + 1, Functor>::go(rhs, lhs);
		}
	};
	
	template<>
	struct each_dim_2<Dims>
	{
		static void go(basic_hypercube&, basic_hypercube&)
		{}
	};
	
	template<int Dim, typename Functor>
	struct each_dim_or_2
	{
		static bool go(basic_hypercube& rhs, basic_hypercube& lhs)
		{
			if(Functor::template go<Dim>(rhs, lhs))
				return true;
			return each_dim<Dim + 1, Functor>::go(rhs, lhs);
		}
	};
	
	template<>
	struct each_dim_or_2<Dims>
	{
		static bool go(basic_hypercube&, basic_hypercube&)
		{
			return false;
		}
	};
	
	
	template<int Dim, typename Functor>
	struct each_dim_and_2
	{
		static bool go(basic_hypercube& rhs, basic_hypercube& lhs)
		{
			if(!Functor::template go<Dim>(rhs, lhs))
				return false;
			return each_dim<Dim + 1, Functor>::go(rhs, lhs);
		}
	};
	
	template<>
	struct each_dim_and_2<Dims>
	{
		static bool go(basic_hypercube&, basic_hypercube&)
		{
			return true;
		}
	};
	
	basic_hypercube()
	{
	}

	basic_hypercube()
	: x1(x1_), y1(y1_), x2(x2_), y2(y2_)
	{

	}
	
/*
	basic_rect(basic_vec<T> const& center, T size)
	: x1(center.x - size), y1(center.y - size)
	, x2(center.x + size), y2(center.y + size)
	{

	}
	
	basic_rect(basic_vec<T> const& center, T w, T h)
	: x1(center.x - w/2), y1(center.y - h/2)
	, x2(center.x + w/2), y2(center.y + h/2)
	{

	}
	

	basic_rect(basic_vec<T> const& corner1, basic_vec<T> const& corner2)
	{
		if(corner1.x < corner2.x)
		{
			x1 = corner1.x;
			x2 = corner2.x + 1;
		}
		else
		{
			x1 = corner2.x;
			x2 = corner1.x + 1;
		}
		
		if(corner1.y < corner2.y)
		{
			y1 = corner1.y;
			y2 = corner2.y + 1;
		}
		else
		{
			y1 = corner2.y;
			y2 = corner1.y + 1;
		}
	}*/
		
	interval i[Dims];
	
	template<int Dim>
	T center_comp() const
	{
		return (i[Dim].b + i[Dim].e) / T(2);
	}
	
	template<int Dim>
	T length_comp()
	{
		return i[Dim].e - i[Dim].b;
	}

/*
	bool valid()
	{
		return x1 <= x2 && y1 <= y2;
	}*/
	
	VL_DIM_FUNC(void, JoinDim, (basic_hypercube& lhs, basic_hypercube& rhs)
	{
		if(rhs.i[Dim].a < lhs.i[Dim].a)
			lhs.i[Dim].a = rhs.i[Dim].a;
		if(rhs.i[Dim].b > lhs.i[Dim].b)
			lhs.i[Dim].b = rhs.i[Dim].b;
	})
		
	void join(basic_hypercube const& b)
	{
		each_dim_2<0, JoinDim>::go(*this, b);
	}

	VL_DIM_FUNC(bool, IntersectTest, (basic_hypercube& lhs, basic_hypercube& rhs)
	{
		return rhs.i[Dim].e >= lhs.i[Dim].b
		    && rhs.i[Dim].b <= lhs.i[Dim].e);
	})
	
	VL_DIM_FUNC(void, Intersect, (basic_hypercube& lhs, basic_hypercube& rhs)
	{
		if(rhs.i[Dim].b > lhs.i[Dim].b)
			lhs.i[Dim].b = rhs.i[Dim].b;
		if(rhs.i[Dim].e < lhs.i[Dim].e)
			lhs.i[Dim].e = rhs.i[Dim].e;
	})

	bool intersecting(basic_hypercube const& b) const
	{
		return each_dim_and_2<0, IntersectTest>::go(*this, b);
	}

	bool intersect(basic_hypercube const& b)
	{
		if(!intersecting(b))
		{
			return false;
		}
		
		each_dim_2<0, Intersect>::go(*this, b);
		
		return true;
	}

/*
	bool inside(basic_vec<T> v) const
	{
		T diffX = v.x - x1;
		T diffY = v.y - y1;
		
		return diffX < width() && diffX >= T(0)
		    && diffY < height() && diffY >= T(0);
		
	}
	*/
	
	basic_rect operator&(basic_hypercube const& b) const
	{
		return basic_rect(*this) &= b;
	}
	
	basic_rect& operator&=(basic_hypercube const& b)
	{
		each_dim_2<0, Intersect>::go(*this, b);
		
		return *this;
	}
	
	/*
	basic_rect operator+(basic_vec<T> const& b)
	{
		return basic_rect(*this) += b;
	}
	
	basic_rect& operator+=(basic_vec<T> const& b)
	{
		x1 += b.x;
		x2 += b.x;
		y1 += b.y;
		y2 += b.y;
		
		return *this;
	}
	
	basic_rect operator-(basic_vec<T> const& b)
	{
		return basic_rect(*this) -= b;
	}
	
	basic_rect& operator-=(basic_vec<T> const& b)
	{
		x1 -= b.x;
		x2 -= b.x;
		y1 -= b.y;
		y2 -= b.y;
		
		return *this;
	}
	
	basic_rect translate(T x, T y) // TODO: This should be called "translated"
	{
		return basic_rect(x1 + x, y1 + y, x2 + x, y2 + y);
	}
	
	void translate_v(T y)
	{
		y1 += y;
		y2 += y;
	}
	
	basic_vec<T> ul()
	{
		return basic_vec<T>(x1, y1);
	}
	
	basic_vec<T> ur()
	{
		return basic_vec<T>(x2, y1);
	}
	
	basic_vec<T> ll()
	{
		return basic_vec<T>(x1, y2);
	}
	
	basic_vec<T> lr()
	{
		return basic_vec<T>(x2, y2);
	}*/
};

typedef basic_hypercube<2, int> hrect;
typedef basic_hypercube<3, int> hcube;

#undef VL_DIM_FUNC

}

#endif // VL_HYPERCUBE_HPP
